package com.atguigu.gmall.cart.service;

import com.atguigu.gmall.cart.entity.CartItem;
import com.atguigu.gmall.cart.feign.GmallPmsClient;
import com.atguigu.gmall.cart.feign.GmallSmsClient;
import com.atguigu.gmall.cart.feign.GmallWmsClient;
import com.atguigu.gmall.cart.interceptor.LoginInterceptor;
import com.atguigu.gmall.cart.mapper.CartItemMapper;
import com.atguigu.gmall.cart.vo.UserInfo;
import com.atguigu.gmall.common.bean.ResponseVo;
import com.atguigu.gmall.common.exception.GmallException;
import com.atguigu.gmall.pms.entity.SkuAttrValueEntity;
import com.atguigu.gmall.pms.entity.SkuEntity;
import com.atguigu.gmall.sms.vo.ItemSaleVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.concurrent.ListenableFuture;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.stream.Collectors;

/*

 */
@Service
public class CartItemService {
    @Autowired
    StringRedisTemplate redisTemplate; //键和值必须是String类型
    String cartPrefix = "cart:items:";
    String currentPriceSkuPrefix = "cart:sku:price";
    Gson gson = new Gson();
//    @Autowired
//    CartItemMapper cartItemMapper;
    @Autowired
    GmallPmsClient pmsClient;
    @Autowired
    GmallWmsClient wmsClient;
    @Autowired
    GmallSmsClient smsClient;
    @Autowired
    CartItemAsyncService cartItemAsyncService;
    /*
        添加商品到购物车的业务方法
     */
    public void add2Cart(Long skuId, Integer count) {
        //1、获取请求客户端的 userId，如果没有获取userKey(userId)
        String userId = getUserId();
        //2、使用userId拼接redis中购物车的key：cart:items:userId 获取购物车的hash结构(Map<skuId,CartItem>)
        //使用redis中的key获取它的hash结构
        BoundHashOperations<String, Object, Object> hashOps = redisTemplate.boundHashOps(cartPrefix + userId);
        if (hashOps.hasKey(skuId.toString())) {
            //3、如果购物车中包含skuId对应的购物项(skuId和购物项 1:1)   hash中如果包含skuId的key
            //   获取购物车中的skuId的购物项CartItem： 修改CartItem的count(cartItem之前的count+count)
            String cartItemJson = hashOps.get(skuId.toString()).toString();//获取skuId购物项的json字符串
            CartItem cartItem = gson.fromJson(cartItemJson, CartItem.class);
            cartItem.setCount(cartItem.getCount() + count);
            //    redis+mysql
            hashOps.put(skuId.toString(), gson.toJson(cartItem));
            //以后修改为异步操作：SpringTask 异常监听  操作失败后可以监听到失败的信息，将失败信息缓存到redis中
            //最后通过定时任务 加载redis中mysql操作失败的信息  ，重新同步redis购物车数据到mysql中
            cartItemAsyncService.updateCartItemByUserIdAndSkuId(userId , skuId ,cartItem);

        } else {
            //4、如果购物车中没有skuId对应的购物项：初始化cartItem对象(根据skuId查询cartItem需要的数据设置给它)
            //  将skuId和初始化后的cartItem 保存到购物车中
            CartItem cartItem = new CartItem();
            //需要skuEntity的数据、 需要sku的销售属性、需要sku的促销属性
            ResponseVo<SkuEntity> skuEntityResponseVo = pmsClient.querySkuById(skuId);
            ResponseVo<List<SkuAttrValueEntity>> skuAttrValuesBySkuId = pmsClient.querySkuAttrValuesBySkuId(skuId);
            ResponseVo<List<ItemSaleVo>> itemSalesBySkuId = smsClient.queryItemSalesBySkuId(skuId);
            ResponseVo<Boolean> booleanResponseVo = wmsClient.queryItemSkuStoreBySkuId(skuId);
            SkuEntity skuEntity = skuEntityResponseVo.getData();
            List<SkuAttrValueEntity> skuAttrValueEntities = skuAttrValuesBySkuId.getData();
            List<ItemSaleVo> itemSaleVos = itemSalesBySkuId.getData();
            Boolean store = booleanResponseVo.getData();
            if (skuEntity != null) {
                cartItem.setPrice(skuEntity.getPrice());
                cartItem.setCurrentPrice(skuEntity.getPrice());
                //为了方便同步所有用户的购物车购物项的实时价格，使用skuId作为键 实时价格作为值存入到redis中
                redisTemplate.opsForValue().set(currentPriceSkuPrefix+skuId , skuEntity.getPrice().toString());
                cartItem.setTitle(skuEntity.getTitle());
                cartItem.setDefaultImage(skuEntity.getDefaultImage());
            }
            if (!CollectionUtils.isEmpty(skuAttrValueEntities)) {
                cartItem.setSaleAttrs(gson.toJson(skuAttrValueEntities));//转为json字符串
            }
            if (!CollectionUtils.isEmpty(itemSaleVos)) {
                cartItem.setSales(gson.toJson(itemSaleVos));//转为json字符串
            }
            if (store != null) {
                cartItem.setStore(store);
            }
            cartItem.setCount(count);
            cartItem.setUserId(userId);
            cartItem.setSkuId(skuId.toString());
            cartItem.setCheck(true);//默认选中
            //   redis+mysql
            hashOps.put(skuId.toString(), gson.toJson(cartItem));
            cartItemAsyncService.saveCartItem(userId , cartItem);

        }

    }


    public String getUserId() {
        UserInfo userInfo = LoginInterceptor.getUserInfo();
        if (StringUtils.isNotBlank(userInfo.getUserId())) {
            return userInfo.getUserId();
        }
        return userInfo.getUserKey();
    }

    public CartItem queryCartItemBySkuId(Long skuId) {
        String userId = getUserId();
        //先获取购物车  再获取购物项
        BoundHashOperations<String, Object, Object> hashOps = redisTemplate.boundHashOps(cartPrefix + userId);
        if (hashOps.hasKey(skuId.toString())) {
            return gson.fromJson(hashOps.get(skuId.toString()).toString(), CartItem.class);
        }
        //再使用userKey查询购物项方法
        BoundHashOperations<String, Object, Object> userKeyhashOps =
                redisTemplate.boundHashOps(cartPrefix + LoginInterceptor.getUserInfo().getUserKey());
        if (userKeyhashOps.hasKey(skuId.toString())) {
            return gson.fromJson(userKeyhashOps.get(skuId.toString()).toString(), CartItem.class);
        }
        throw new GmallException(skuId + " 商品对应的购物项不存在");
    }

    //    @Async
//    public ListenableFuture<String> exec1() {
//        System.out.println("exec1开始执行：..."+new Date());
//        try {
//            Thread.sleep(3000);
//            System.out.println("exec1执行结束：..."+new Date());
//            return AsyncResult.forValue("exec1的result");//没有异常正常的返回结果
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//            return AsyncResult.forExecutionException(e);//异常时的返回结果
//        }
//    }
//    @Async
//    public ListenableFuture<String> exec2() {
//        System.out.println("exec2开始执行：..."+new Date());
//        try {
//            Thread.sleep(5000);
//            int i =1/0;
//            System.out.println("exec2执行结束：..."+new Date());
//            return AsyncResult.forValue("exec2的结果");
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//            return AsyncResult.forExecutionException(e);
//        }
//    }
    @Async
    public String exec1() {
        System.out.println("exec1开始执行：..." + new Date());
        try {
            Thread.sleep(3000);
            System.out.println("exec1执行结束：..." + new Date());
            return "exec1的result";//没有异常正常的返回结果
        } catch (InterruptedException e) {
            e.printStackTrace();
            return null;
        }
    }

    @Async
    public String exec2() {
        System.out.println("exec2开始执行：..." + new Date());
        try {
            Thread.sleep(5000);
            int i = 1 / 0;
            System.out.println("exec2执行结束：..." + new Date());
            return "exec2的结果";
        } catch (Exception e) {
//            e.printStackTrace();//输出异常堆栈信息
            //如果异常希望交给Asyn异常处理器处理，必须手动抛出异常
            throw new GmallException(e.getMessage());
//            return null;
        }
    }

    public List<CartItem> queryCartItems() {
        UserInfo userInfo = LoginInterceptor.getUserInfo();
        String userKey = userInfo.getUserKey();
        //1、获取客户端的userKey 查询redis中userKey的购物车   未登录购物车
        BoundHashOperations<String, Object, Object> unLoginHashOps = redisTemplate.boundHashOps(cartPrefix + userKey);
//        unLoginHashOps.keys(): 获取hash结构所有的键   skuId->cartItem     skuId集合就是keys集合
        List<Object> unloginCartItemJsons = unLoginHashOps.values(); //未登录购物项json字符串集合
        String userId = userInfo.getUserId();
        List<CartItem> unloginCartItems = null;
        //2、获取客户端的userId  如果没有 代表用户未登录   直接返回未登录购物车购物项集合
        //将未登录购物项集合json转为 购物项对象集合返回
        if(!CollectionUtils.isEmpty(unloginCartItemJsons)){
            unloginCartItems =  unloginCartItemJsons.stream().map(unloginCartItemJson ->{
                CartItem cartItem = gson.fromJson(unloginCartItemJson.toString(), CartItem.class);
                //为未登录购物车的购物项查询实时价格设置
                String currentPrice = redisTemplate.opsForValue().get(currentPriceSkuPrefix + cartItem.getSkuId());
                cartItem.setCurrentPrice(new BigDecimal(currentPrice));
                return cartItem;
            }).collect(Collectors.toList());
        }
        if(StringUtils.isEmpty(userId)){
            return unloginCartItems;
        }
        //3、如果userId存在 代表已登录  查询redis中的userId的购物车   已登录购物车
        BoundHashOperations<String, Object, Object> loginHashOps = redisTemplate.boundHashOps(cartPrefix + userId);
        //4、如果未登录购物车不为空 需要将未登录购物车合并到 userId 已登录购物车中
        if(!CollectionUtils.isEmpty(unloginCartItems)){
            //遍历未登录购物车合并到已登录购物车中
            unloginCartItems.forEach(unloginCartItem->{
                if(loginHashOps.hasKey(unloginCartItem.getSkuId().toString())){
                    //判断如果已登录购物车中包含该购物项，更新userId购物车购物项数量
                    String loginCartItemJson = loginHashOps.get(unloginCartItem.getSkuId().toString()).toString();
                    CartItem loginCartItem = gson.fromJson(loginCartItemJson, CartItem.class);
                    //更新数量
                    loginCartItem.setCount(loginCartItem.getCount()+unloginCartItem.getCount());
                    //更新到redis 和mysql 中
                    loginHashOps.put(unloginCartItem.getSkuId().toString() ,  gson.toJson(loginCartItem) );
                    // TODO: 异步更新数据库中购物项的数量
                    cartItemAsyncService.updateCartItemByUserIdAndSkuId(userId,Long.parseLong(loginCartItem.getSkuId()),loginCartItem);
                }else{
                    //如果已登录购物车没有该购物项则新增该购物项
                    unloginCartItem.setUserId(userId);//使用userId替换未登录购物车购物项的userKey
                    loginHashOps.put(unloginCartItem.getSkuId().toString() , gson.toJson(unloginCartItem) );
                    // TODO: 异步保存新增购物项到数据库
                    cartItemAsyncService.saveCartItem(userId , unloginCartItem);
                }
            });
            //删除userKey的未登录购物车购物项列表：  redis mysql
            // 删除userKey的购物车购物项：  redis  mysql
            redisTemplate.delete(cartPrefix+userKey);
            //TODO： 异步删除数据库 userKey的购物项数据
            cartItemAsyncService.deleteCartItemsByUserKey(userKey);

        }
        //5、返回已登录购物车 购物项集合
        List<Object> loginCartItemJsons = loginHashOps.values();
        if(!CollectionUtils.isEmpty(loginCartItemJsons)){
            return loginCartItemJsons.stream().map(loginCartItemJson->{
                CartItem cartItem = gson.fromJson(loginCartItemJson.toString(), CartItem.class);
                String currentPrice = redisTemplate.opsForValue().get(currentPriceSkuPrefix + cartItem.getSkuId());
                cartItem.setCurrentPrice(new BigDecimal(currentPrice));
                return cartItem;
            }).collect(Collectors.toList());
        }

        return null;
    }
    //更新购物项数量
    public void updateNum(CartItem cartItem) {
        //获取userId/key
        String userId = getUserId();
        //从redis中获取要更新的购物项
        try {
            BoundHashOperations<String, Object, Object> hashOps = redisTemplate.boundHashOps(cartPrefix + userId);
            String cartItemJson = hashOps.get(cartItem.getSkuId().toString()).toString();
            CartItem redisCartItem = gson.fromJson(cartItemJson, CartItem.class);
            redisCartItem.setCount(cartItem.getCount());
            hashOps.put(cartItem.getSkuId().toString() , gson.toJson(redisCartItem));
            //异步更新数据库
            cartItemAsyncService.updateCartItemByUserIdAndSkuId(userId , Long.parseLong(cartItem.getSkuId()) , redisCartItem);
        } catch (Exception e) {
            e.printStackTrace();//打印异常堆栈信息：可以定位到哪一行代码出现异常(优先查找service中的异常行)
            throw new GmallException(e.getMessage());
        }


    }

    public void deleteCart(String skuId) {
        try {
            String userId = getUserId();
            BoundHashOperations<String, Object, Object> hashOps = redisTemplate.boundHashOps(cartPrefix + userId);
            hashOps.delete(skuId);
            cartItemAsyncService.deleteCartItemBySkuIdAndUserId(skuId,userId);
        } catch (Exception e) {
            e.printStackTrace();
            //统一处理异常
        }
    }

    public boolean updateStatus(CartItem cartItem) {//cartItem代表客户端发送请求时提交的cartItem参数对象
        try {
            //1、获取当前用户的购物车
            String userId = getUserId();
            BoundHashOperations<String, Object, Object> hashOps = redisTemplate.boundHashOps(cartPrefix + userId);
            //2、获取购物车中需要更新的购物项
            String cartItemJson = hashOps.get(cartItem.getSkuId()).toString();
            CartItem item = gson.fromJson(cartItemJson, CartItem.class);
            item.setCheck(cartItem.getCheck());
            //3、更新购物项的选中状态 设置到redis的用户的购物车中
            hashOps.put(item.getSkuId().toString() , gson.toJson(item) );
            cartItemAsyncService.updateCartItemByUserIdAndSkuId(userId,Long.parseLong(item.getSkuId()) , cartItem);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }


    }

    public List<CartItem> queryCheckedCartItemsByUserId(Long userId) {
        //获取用户的购物车
        BoundHashOperations<String, Object, Object> hashOps = redisTemplate.boundHashOps(cartPrefix + userId);
        if(hashOps.size()==0){
            return null;
        }
        List<Object> cartItemJsons = hashOps.values();
        //获取所有的购物项  筛选被选中的购物项集合
        List<CartItem> cartItems = cartItemJsons.stream().map(cartItemJson -> {
            CartItem cartItem = gson.fromJson(cartItemJson.toString(), CartItem.class);
            return cartItem;
        }).filter(cartItem -> cartItem.getCheck()).collect(Collectors.toList());
        return cartItems;
    }
}